S01-27 JavaSE-正则
[TOC]
为什么要学习正则表达式
极速体验正则表达式威力
Regexp_.java:
- 提取文章中所有的英文单词
- 提取文章中所有的数字
- 提取文章中所有的英文单词和数字
- 提取百度热榜标题
java
//示例:提取英文单词
Pattern pattern = Pattern.compile("[a-zA-Z]+");
//提取数字
//Pattern pattern = Pattern.compile("[0-9]+");
//提取英文单词和数字
//Pattern pattern = Pattern.compile("([0-9]+)|([a-zA-Z]+)");
//提取百度热榜标题
//Pattern pattern = Pattern.compile("<a target=\"_blank\" title=\"(\\S*)\"");结论:正则表达式是处理文本的利器。
常见问题
- 找出所有四个数字连在一起的子串?
- 找出所有四个数字连在一起的子串,且第一位与第四位相同、第二位与第三位相同(如1221、5775)?
- 验证输入的邮件是否符合电子邮件格式?
- 验证输入的手机号是否符合手机号格式?
解决之道-正则表达式
- Java提供了正则表达式技术,专门用于处理文本匹配问题。
- 正则表达式:Regular Expression(RegExp),是对字符串执行模式匹配的技术。
- 跨语言特性:不仅Java支持,Python、JavaScript、C#等多数编程语言都支持。
正则表达式基本介绍
- 一个正则表达式是用某种模式去匹配字符串的公式,看似复杂但灵活高效,能将数小时的文本处理工作缩短到几分钟。
- 核心作用:字符串匹配(查找、替换、验证、分割)。
正则表达式底层实现
实例分析
RegTheory.java:分析正则表达式匹配的底层原理
java
package com.hspedu.regexp;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author 韩顺平
* @version 1.0
* 分析java 的正则表达式的底层实现(重要)
*/
public class RegTheory {
public static void main(String[] args) {
String content = "1998 年12 月8 日,第二代Java 平台的企业版J2EE 发布。1999 年6 月,Sun 公司发布了" +
"第二代Java 平台(简称为Java2)的3 个版本:J2ME(Java2 Micro Edition,Java2 平台的微型" +
"版),应用于移动、无线及有限资源的环境;J2SE(Java 2 Standard Edition,Java 2 平台的" +
"标准版),应用于桌面环境;J2EE(Java 2Enterprise Edition,Java 2 平台的企业版),应" +
"用3443 于基于Java 的应用服务器。Java 2 平台的发布,是Java 发展过程中最重要的一个" +
"里程碑,标志着Java 的应用开始普及9889 ";
//目标:匹配所有四个数字
String regStr = "(\\d\\d)(\\d\\d)"; //分组:(第一组)(第二组)
//1. 创建模式对象(正则表达式对象)
Pattern pattern = Pattern.compile(regStr);
//2. 创建匹配器:按照正则表达式规则匹配content字符串
Matcher matcher = pattern.matcher(content);
//3. 开始匹配
/**
* matcher.find() 完成的任务(考虑分组):
* 1. 根据指定规则定位满足规则的子字符串(如(19)(98))
* 2. 找到后,将子字符串的开始索引记录到matcher的groups数组:
* groups[0] = 开始索引,groups[1] = 结束索引+1(整个匹配串)
* groups[2] = 第一组开始索引,groups[3] = 第一组结束索引+1
* groups[4] = 第二组开始索引,groups[5] = 第二组结束索引+1
* 3. 记录oldLast为子字符串的结束索引+1,下次find从该位置开始
*
* matcher.group(int group):
* 根据groups数组的索引,截取子字符串返回
* group(0):整个匹配串(groups[0]~groups[1])
* group(1):第一组匹配串(groups[2]~groups[3])
* group(2):第二组匹配串(groups[4]~groups[5])
*/
while (matcher.find()) {
System.out.println("找到: " + matcher.group(0));
System.out.println("第1 组()匹配到的值 =" + matcher.group(1));
System.out.println("第2 组()匹配到的值=" + matcher.group(2));
}
}
}正则表达式语法
正则表达式的元字符从功能上分为6类:字符匹配符、选择匹配符、限定符、分组组合和反向引用符、特殊字符、定位符。
元字符-转义号\\
在匹配特殊字符(如.*+()$/\?[]^{})时,需要使用转义符\\,否则会被当作正则元字符解析。
注意:在Java中,\\代表其他语言中的一个\。
示例:
java
package com.hspedu.regexp;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author 韩顺平
* @version 1.0
* 演示转义字符的使用
*/
public class RegExp02 {
public static void main(String[] args) {
String content = "abc$(a.bc(123( )";
//匹配( => \\(
//String regStr = "\\(";
//匹配. => \\.
//String regStr = "\\.";
//匹配3个数字
String regStr = "\\d{3}";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println("找到: " + matcher.group(0));
}
}
}元字符-字符匹配符
| 符号 | 含义 | 示例 | 说明 | 匹配输入 |
|---|---|---|---|---|
[] | 可接收的字符列表 | [efgh] | e、f、g、h中的任意1个字符 | e、f、g、h |
[^] | 不接收的字符列表 | [^abc] | 除a、b、c之外的任意1个字符(含数字和特殊符号) | d、1、# |
- | 连字符 | A-Z | 任意单个大写字母 | A、B、Z |
. | 匹配除\n以外的任何字符 | a..b | 以a开头、b结尾,中间2个任意字符(长度为4) | aaab、a35b、a#*b |
\\d | 匹配单个数字字符(等价于[0-9]) | \\d{3}(\\d)? | 3个或4个数字 | 123、9876 |
\\D | 匹配单个非数字字符(等价于[^0-9]) | \\D(\\d)* | 非数字开头,后接任意数字 | a、A342 |
\\w | 匹配数字、大小写字母、下划线(等价于[0-9a-zA-Z_]) | \\d{3}\\w{4} | 3个数字开头,共7位数字字母下划线 | 234abcd、12345Pe |
\\W | 匹配非数字、大小写字母、下划线(等价于[^0-9a-zA-Z_]) | \\W+\\d{2} | 至少1个非数字字母开头,2个数字结尾 | #29、#?@10 |
\\s | 匹配任意空白字符(空格、制表符等) | a\\sb | a和b之间有一个空白字符 | a b、a\tb |
\\S | 匹配任意非空白字符(与\\s相反) | a\\Sb | a和b之间有一个非空白字符 | aab、a#b |
示例:
java
package com.hspedu.regexp;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author 韩顺平
* @version 1.0
* 演示字符匹配符的使用
*/
public class RegExp03 {
public static void main(String[] args) {
String content = "a11c8abc _ABCy @";
//String regStr = "[a-z]";//匹配a-z之间任意一个字符
//String regStr = "[A-Z]";//匹配A-Z之间任意一个字符
//String regStr = "abc";//匹配abc字符串(默认区分大小写)
//String regStr = "(?i)abc";//匹配abc字符串(不区分大小写)
//String regStr = "[0-9]";//匹配0-9之间任意一个字符
//String regStr = "[^a-z]";//匹配不在a-z之间任意一个字符
//String regStr = "[^0-9]";//匹配不在0-9之间任意一个字符
//String regStr = "[abcd]";//匹配在abcd中任意一个字符
//String regStr = "\\w";//匹配大小写英文字母、数字、下划线
//String regStr = "\\W";//匹配等价于[^a-zA-Z0-9_]
//String regStr = "\\s";//匹配任何空白字符
//String regStr = "\\S";//匹配任何非空白字符
String regStr = ".";//匹配除\n之外的所有字符(匹配.本身需用\\.)
//创建Pattern时指定Pattern.CASE_INSENSITIVE表示不区分大小写
Pattern pattern = Pattern.compile(regStr/*, Pattern.CASE_INSENSITIVE*/);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println("找到: " + matcher.group(0));
}
}
}元字符-选择匹配符
| 符号 | 含义 | 示例 | 说明 | 匹配输入 |
|---|---|---|---|---|
| ` | ` | 匹配“ | ”之前或之后的表达式 | `ab |
示例:
java
package com.hspedu.regexp;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author 韩顺平
* @version 1.0
* 选择匹配符
*/
public class RegExp04 {
public static void main(String[] args) {
String content = "hanshunping 韩顺平 寒冷";
String regStr = "han|韩|寒";//匹配han、韩或寒
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println("找到: " + matcher.group(0));
}
}
}元字符-限定符
用于指定其前面的字符或组合项连续出现的次数。
| 符号 | 含义 | 示例 | 说明 | 匹配输入 |
|---|---|---|---|---|
* | 0次或多次(零到多) | (abc)* | 任意个abc(含0个) | ""、abc、abcabc |
+ | 1次或多次(一到多) | m+(abc)* | 至少1个m开头,后接任意个abc | m、mabc、mabcabc |
? | 0次或1次(最多一次) | m+abc? | 至少1个m开头,后接ab或abc | mab、mabc、mmab |
{n} | 恰好n次 | [abcd]{3} | 由abcd中字母组成的长度为3的字符串 | abc、dbc、ade |
{n,} | 至少n次 | [abcd]{3,} | 由abcd中字母组成的长度≥3的字符串 | abc、abcd、aaabdc |
{n,m} | 至少n次,最多m次 | [abcd]{3,5} | 由abcd中字母组成的长度3~5的字符串 | abc、abcd、aaaaa |
注意:Java匹配默认是贪婪匹配(尽可能匹配多的字符)。
示例:
java
package com.hspedu.regexp;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author 韩顺平
* @version 1.0
* 演示限定符的使用
*/
public class RegExp05 {
public static void main(String[] args) {
String content = "a211111aaaaaahello";
//String regStr = "a{3}";//匹配aaa
//String regStr = "1{4}";//匹配1111
//String regStr = "\\d{2}";//匹配两位数字
//String regStr = "a{3,4}";//匹配aaa或aaaa(贪婪匹配)
//String regStr = "1{4,5}";//匹配1111或11111
//String regStr = "\\d{2,5}";//匹配2~5位数字
//String regStr = "1+";//匹配1个或多个1
//String regStr = "\\d+";//匹配1个或多个数字
//String regStr = "1*";//匹配0个或多个1
String regStr = "a1?";//匹配a或a1
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println("找到: " + matcher.group(0));
}
}
}元字符-定位符
规定要匹配的字符串出现的位置(起始、结束、边界)。
| 符号 | 含义 | 示例 | 说明 | 匹配输入 |
|---|---|---|---|---|
^ | 指定起始位置 | ^[0-9]+[a-z]* | 以至少1个数字开头,后接任意个小写字母 | 123、6aa、555edf |
$ | 指定结束位置 | ^[0-9]+-[a-z]+$ | 以1个数字开头,接连字符“-”,以至少1个小写字母结尾 | 1-a、123-abc |
\\b | 匹配目标字符串的边界 | han\\b | 子串间有空格或目标字符串结束的位置 | hanshunping(末尾han)、sp han(空格后han) |
\\B | 匹配目标字符串的非边界 | han\\B | 与\\b相反(子串内部) | hanshunping(开头han)、nhan(n后han) |
示例:
java
package com.hspedu.regexp;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author 韩顺平
* @version 1.0
* 演示定位符的使用
*/
public class RegExp06 {
public static void main(String[] args) {
String content = "hanshunping sphan nnhan";
//String content = "123-abc";
//以至少1个数字开头,后接任意个小写字母
//String regStr = "^[0-9]+[a-z]*";
//以至少1个数字开头,必须以至少1个小写字母结尾
//String regStr = "^[0-9]+\\-[a-z]+$";
//匹配边界的han(空格后或字符串末尾)
//String regStr = "han\\b";
//匹配非边界的han(子串内部)
String regStr = "han\\B";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println("找到: " + matcher.group(0));
}
}
}分组组合和反向引用符
| 分组构造形式 | 说明 |
|---|---|
(pattern) | 非命名捕获:捕获匹配的子字符串,编号从0开始(0为整个表达式,1~n为分组) |
(?<name>pattern) | 命名捕获:将匹配的子字符串捕获到指定名称的组中(name不能含标点,不能以数字开头) |
(?:pattern) | 非捕获匹配:匹配pattern但不存储结果,用于组合模式 |
(?=pattern) | 正向预查:匹配pattern前面的位置(不捕获pattern) |
(?!pattern) | 负向预查:匹配不处于pattern前面的位置(不捕获pattern) |
示例1:命名分组
java
package com.hspedu.regexp;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author 韩顺平
* @version 1.0
* 分组:命名分组和非命名分组
*/
public class RegExp07 {
public static void main(String[] args) {
String content = "hanshunping s7789 nn1189han";
//非命名分组:(\\d\\d)(\\d\\d)
//命名分组:(?<g1>\\d\\d)(?<g2>\\d\\d)
String regStr = "(?<g1>\\d\\d)(?<g2>\\d\\d)";//匹配4个数字
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println("找到: " + matcher.group(0));
System.out.println("第1个分组内容=" + matcher.group(1));
System.out.println("第1个分组内容[通过组名]=" + matcher.group("g1"));
System.out.println("第2个分组内容=" + matcher.group(2));
System.out.println("第2个分组内容[通过组名]=" + matcher.group("g2"));
}
}
}示例2:非捕获分组
java
package com.hspedu.regexp;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author 韩顺平
* @version 1.0
* 演示非捕获分组
*/
public class RegExp08 {
public static void main(String[] args) {
String content = "hello 韩顺平教育jack 韩顺平老师韩顺平同学hello 韩顺平学生";
//需求1:找到韩顺平教育、韩顺平老师、韩顺平同学(非捕获分组)
//String regStr = "韩顺平(?:教育|老师|同学)";
//需求2:找到韩顺平教育和韩顺平老师中的"韩顺平"(正向预查)
//String regStr = "韩顺平(?=教育|老师)";
//需求3:找到不是韩顺平教育和韩顺平老师中的"韩顺平"(负向预查)
String regStr = "韩顺平(?!教育|老师)";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println("找到: " + matcher.group(0));
}
}
}应用实例
字符串验证(RegExp10.java)
验证以下格式:
- 汉字:
^[\u0391-\uffe5]+$ - 邮政编码:1-9开头的6位数:
^[1-9]\\d{5}$ - QQ号码:1-9开头的5-10位数:
^[1-9]\\d{4,9}$ - 手机号码:13、14、15、18开头的11位数:
^1[3|4|5|8]\\d{9}$
java
package com.hspedu.regexp;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author 韩顺平
* @version 1.0
* 正则表达式的应用实例:字符串验证
*/
public class RegExp10 {
public static void main(String[] args) {
String content = "13588889999";
//验证汉字
//String regStr = "^[\\u0391-\\uffe5]+$";
//验证邮政编码
//String regStr = "^[1-9]\\d{5}$";
//验证QQ号码
//String regStr = "^[1-9]\\d{4,9}$";
//验证手机号码
String regStr = "^1[3|4|5|8]\\d{9}$";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
if(matcher.find()) {
System.out.println("满足格式");
} else {
System.out.println("不满足格式");
}
}
}URL验证(RegExp11.java)
URL格式:^((http|https)://)?([\\w-]+\\.)+[\\w-]+(\\/[\\w-?=&/%.#]*)?$
java
package com.hspedu.regexp;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author 韩顺平
* @version 1.0
* 演示URL验证
*/
public class RegExp11 {
public static void main(String[] args) {
String content = "https://www.bilibili.com/video/BV1fh411y7R8?from=search&seid=1831060912083761326";
//String content = "http://edu.3dsmax.tech/yg/bilibili/my6652/pc/qg/05-51/index.html#201211-1?track_id=xxx";
/**
* 思路分析:
* 1. 协议部分:(http|https):// (可选)
* 2. 域名部分:([\\w-]+\\.)+[\\w-]+ (如www.bilibili.com)
* 3. 路径和参数部分:(\\/[\\w-?=&/%.#]*)? (可选,如/video/BV1fh411y7R8?from=search)
*/
String regStr = "^((http|https)://)?([\\w-]+\\.)+[\\w-]+(\\/[\\w-?=&/%.#]*)?$";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
System.out.println("是否满足URL格式:" + matcher.find());
//或